﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace wRPCService
{
    /// <summary>
    /// Aop组件，目前仅支持特性，不支持全局，可以写在类上
    /// </summary>
    public class Aop
    {
        /// <summary>
        /// 调用指定方法
        /// </summary>
        /// <param name="method">方法信息</param>
        /// <param name="obj">实例对象</param>
        /// <param name="parameters">方法参数</param>
        /// <returns>返回值</returns>
        public static object Invoke(MethodInfo method, object obj, object[] parameters)
        {
            //获取所有AOP特性
            var aopMethod = method.GetCustomAttributes<AopAttribute>(true);
            var aopClass = method.ReflectedType.GetCustomAttributes<AopAttribute>(true).Where(d =>
            {
                if (aopMethod.Any(t => t.TypeId == d.TypeId))
                {
                    var usage = d.GetType().GetCustomAttribute<AttributeUsageAttribute>();
                    return (usage == null || usage.AllowMultiple);
                }
                else return true;
            });
            var aop = aopMethod.Union(aopClass).OrderBy(d=>d.Order).ToList();
            //组装上下文
            var context = new AopContext { Method = method, Instance = obj, Parameters = parameters };
            int maxAop = -1;//有效的AOP数量，如果有AOP返回false表示后续及方法体均不支持
            bool needExecute = true;//是否需要执行方法体
            bool isAsync = method.GetCustomAttribute<AsyncStateMachineAttribute>() != null;//是否是异步方法
            //OnActionExecuting
            for (int i = 0; i < aop.Count(); i++)
            {
                //记录最后一个运行的特性，如果返回false则不执行之后的
                maxAop = i;
                needExecute = aop[i].OnActionExecuting(context);
                if (!needExecute) break;
            }
            //运行方法体
            if (needExecute)
            {
                try
                {
                    var o = method.Invoke(obj, parameters);
                    if (isAsync) context.Result = (o as Task).GetType().GetProperty("Result").GetValue(o);
                    else context.Result = o;
                }
                catch (Exception err)
                {
                    context.Exception = err;
                }
            }
            //OnActionExecuted
            for (int i = maxAop; i >= 0; i--)
            {
                if (!aop[i].OnActionExecuted(context)) break;
            }
            //最后屏障，防止白页面
            if (context.Result == null && context.Exception != null) throw context.Exception;
            return context.Result;
        }
    }
    /// <summary>
    /// Aop上下文对象
    /// </summary>
    public class AopContext
    {
        /// <summary>
        /// 方法信息
        /// </summary>
        public MethodInfo Method { get; set; }
        /// <summary>
        /// 实例对象
        /// </summary>
        public object Instance { get; set; }
        /// <summary>
        /// 方法参数
        /// </summary>
        public object[] Parameters { get; set; }
        /// <summary>
        /// 返回结果
        /// </summary>
        public object Result { get; set; }
        /// <summary>
        /// 临时变量
        /// </summary>
        public Dictionary<string, object> Data { get; set; } = new Dictionary<string, object>();
        /// <summary>
        /// 异常信息
        /// </summary>
        public Exception Exception { get; set; }
    }
    /// <summary>
    /// Aop特性，所有需要Aop操作的特性均应继承此特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public abstract class AopAttribute : Attribute
    {
        /// <summary>
        /// 排序，越小越外侧运行。
        /// </summary>
        public virtual int Order { get; set; } = 100;
        /// <summary>
        /// 在Action执行之前
        /// </summary>
        /// <param name="context">上下文</param>
        /// <returns>是否继续执行，默认true</returns>
        public virtual bool OnActionExecuting(AopContext context) => true;
        /*
        /// <summary>
        /// 在执行Result之前
        /// </summary>
        /// <param name="context">上下文</param>
        /// <returns>是否继续执行，默认true</returns>
        public abstract void OnResultExecuting(AopContext context);
        /// <summary>
        /// 在执行Result之后
        /// </summary>
        /// <param name="context">上下文</param>
        /// <returns>是否继续执行，默认true</returns>
        public abstract void OnResultExecuted(AopContext context);
         */
        /// <summary>
        /// 在Action执行之后
        /// </summary>
        /// <param name="context">上下文</param>
        /// <returns>是否继续执行，默认true</returns>
        public virtual bool OnActionExecuted(AopContext context) => true;
    }
}